import java.awt.Point;
import java.util.ArrayList;
import java.util.List;

/* The GameState class handles just that, the current state of a game. It keeps track of active player position, and the generated maze
 * these are simply accessible through a number of access functions, and most game functionality is closed. By interfacing with the
 * game state using the "MovePlayer" function, an external system can tell the player to move, and all movement logic will
 * be handled automatically, including being blocked by walls. A player history is also kept for the drawing of a trail following the
 * player. This is just a linked list structure, which contains points.
 */
public class GameState {
	private Maze maze;
	
	private int playerX;
	private int playerY;
			
	// The list of previously visited locations
	private List<Point> playerHistory;
	
	// A simple constructor which automatically generates a maze, and initializes the player
	public GameState ( int maze_w, int maze_h ) {
		maze = MazeGenerator_DFS.generateMaze( maze_w, maze_h );
		setPlayerPosition( getMaze().getStartX(), getMaze().getStartY() );
		playerHistory = new ArrayList<Point>();
	}
	
	// accessor functions for getting values. pretty simple.
	public Maze getMaze () {
		return maze;
	}
	
	public int getPlayerX () {
		return playerX;
	}
	public int getPlayerY () {
		return playerY;
	}
	
	public List<Point> getPlayerHistory() {
		return playerHistory;
	}
	
	public void setPlayerPosition ( int x, int y ) {
		playerX = x;
		playerY = y;
	}
	
	/* This function checks to see whether the player occupies the same grid space as the exit, and simplu returns true
	 * or false.
	 */
	public boolean playerAtExit () {
		return (getPlayerX() == getMaze().getExitX()) && (getPlayerY() == getMaze().getExitY());
	}
	
	/* The getFogOfWar function checks the distance between the player position, and a given point. If it is within the set
	 * "fog distance" parameter, then it is not under the fog of war, if it is greater than this distance, true is returned,
	 * and the drawing code will do what it may with that.
	 */
	public boolean getFogOfWar ( int x, int y, int fogDist ) {
		return Math.sqrt(x*x+y*y) > fogDist;
	}
	
	/* The movePlayer function translates the player coordinates by a given offset, automatically checking whether or not
	 * the destination tile is passable. If it is, then the player will be moved there, otherwise, the player will remain
	 * where they were. A value of true will be returned upon a successful move. False will be returned if the player can not
	 * be moved by the desired amount.
	 */
	public boolean movePlayer ( int x, int y ) {
		// Check if the player can be moved to the desired location (based on wall collision
		if ( getMaze().getTile( getPlayerX()+x, getPlayerY()+y ).isPassable() ) {
			playerHistory.add( new Point( getPlayerX(), getPlayerY() ) );
			setPlayerPosition( getPlayerX() + x, getPlayerY() + y );
			return true;
		}
		return false;
	}
	
	
	/* PrintGame outputs the game to Stdout, drawing it with ASCII characters. 
	 * The exit is marked with "EE", the start is marked with "SS", and walls are defined with "[]"
	 * the player location is drawn with "@@", and a trail is drawn with "--" and " |"..
	 */
	public ArrayList<String> printGame () {
		ArrayList<String> returnLn = new ArrayList<String>();
		
		for ( int y = 0; y < getMaze().getHeight(); y ++ ) {
			String ln = "";
			for ( int x = 0; x < getMaze().getWidth(); x ++ ) {
				if ( x == getPlayerX() && y == getPlayerY() ) {
					ln += "@";
				}else if ( x == getMaze().getExitX() && y == getMaze().getExitY() ) {
					ln += "E";
				}else if( x == getMaze().getStartX() && y == getMaze().getStartY() ) {
					ln += "S";
				}else{
					switch ( getMaze().getTile(x,y) ) {
						case PATH: 
							boolean visited = false;
							for ( int i = 1; i < getPlayerHistory().size(); i ++ ) {
								Point current = getPlayerHistory().get(i);
								Point previous = getPlayerHistory().get(i-1);
								if ( current.x == x && current.y == y ) {
									visited = true;
									if ( previous.x != current.x ) 
										ln += "-";
									else
										ln += "|";
								}
							}
							
							if ( !visited )
								ln += " ";
							break;
						case WALL:
							ln += "X";
							break;
						default:
							ln += "U";
							break;
					}
				}
			}
			returnLn.add(ln);
		}
		
		return returnLn;
	}
}
